home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / MISC / DTMFF110 / FREQ.C < prev    next >
C/C++ Source or Header  |  1997-08-05  |  14KB  |  525 lines

  1. /*
  2.  *    Program: FREQ.C
  3.  *    Author: Philip VanBaren & Emil LAURENTIU
  4.  *    Date: 15 August 1993
  5.  *    Last modified: Tuesday, 05 August 1997
  6.  *
  7.  * Description: This program samples data from a sound card, performs an FFT,
  8.  *    and displays the result.
  9.  *    Can handle up to 2048 points (actually any size is possible
  10.  *    with a little fiddling of buffers to get around 64k limits).
  11.  *    (This restriction is given freq.h, and may be changed.)
  12.  *    On a 486/33 this code can perform and plot 1024-point and
  13.  *    below in nearly real-time at 44100kHz.  (1024 FFT=31ms)
  14.  *
  15.  *  The DOS portion of this code was written for Borland C, but should work
  16.  *  with other compilers if the graphics and console-io base functions are
  17.  *  changed appropriately.
  18.  *
  19.  *  The source for specific graphics environments and sound cards may require
  20.  *  other packages.  Refer to the specific files for more details.
  21.  *
  22.  *  Most changes are required only in the sc_*.c and gr_*.c files.
  23.  *
  24.  *  Copyright (C) 1995    Philip VanBaren (C) Emil LAURENTIU
  25.  */
  26.  
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <math.h>
  31. #include "freq.h"
  32. #include "fft.h"
  33. #include "extern.h"
  34. #include "display.h"
  35.  
  36. /*
  37.  *  Table for approximating the logarithm.
  38.  *  These values are round(log2(index/16)*8192) for index=0:31
  39.  */
  40.  
  41. long          ln[] = {
  42.           -131072L, -32768L, -24576L, -19784L, -16384L, -13747L,
  43.           -11592L, -9770L, -8192L, -6800L, -5555L, -4428L, -3400L,
  44.           -2454L, -1578L, -763L, 0L, 716L, 1392L, 2031L, 2637L, 3214L,
  45.           3764L, 4289L, 4792L, 5274L, 5738L, 6184L, 6614L, 7029L, 7429L,
  46.           7817L };
  47. int          f_dtmf[8] = {697, 770, 852, 941, 1209, 1336, 1477, 1633};
  48. int          o_dtmf[8] = {18, 20, 22, 24, 31, 34, 37, 42};
  49. int          sb_f_dtmf[8] = {457, 505, 558, 617, 396, 438, 484, 535};
  50. char          matrix_dtmf[4][4] = {
  51.           {'1', '2', '3', 'A'},
  52.           {'4', '5', '6', 'B'},
  53.           {'7', '8', '9', 'C'},
  54.           {'*', '0', '#', 'D'}  };
  55. int          on_dtmf[8];
  56. char          dtmf_nr[121];
  57. int          p_dtmf;
  58. int          last_i = -1;
  59. int          active_freq, active_dtmf = 0;
  60. int          active_ctcss = 0;
  61. int          ctcss_nr;
  62. double          f_ctcss[] = {
  63.           67.0, 69.4 , 71.9, 74.4, 77.0, 79.7, 82.5, 85.4, 88.5, 91.5,
  64.           94.8, 97.4, 100.0, 103.5, 107.2, 110.9, 114.8, 118.8, 123.0,
  65.           127.3, 131.8, 136.5, 141.3, 146.2, 151.4, 156.7, 159.8, 162.2,
  66.           165.5, 167.9, 171.3, 173.8, 177.3, 179.9, 183.5, 186.2, 189.9,
  67.           192.8, 196.6, 199.5, 203.5, 206.5, 210.7, 218.1, 225.7, 229.1,
  68.           233.6, 241.8, 250.3, 254.1 };
  69. unsigned long ctcss_act1 = 0xABFFFFFD;
  70. unsigned long ctcss_act2 = 0x0001DD2A;
  71. long          m_ctcss;
  72. char          sline[80];
  73. volatile int  flag[BUFFERS];    /* Array of flags indicating fullness of
  74.                  * buffers */
  75. volatile int  record_buffer;    /* Pointer to next buffer to be filled */
  76. int          queue_buffer;    /* Pointer to next buffer to be queued */
  77. int          process_buffer;    /* Pointer to next buffer to be FFTed */
  78.  
  79. short         *fftdata;        /* Array for FFT data */
  80. short         *wind;        /* Array storing windowing function */
  81.  
  82. int          x[WINDOW_RIGHT - WINDOW_LEFT + 1];    /* Array of bin #'s
  83.                              * displayed */
  84. int          x2[WINDOW_RIGHT - WINDOW_LEFT + 1];    /* Array of terminal bin
  85.                              * #'s */
  86. int          lasty[WINDOW_RIGHT - WINDOW_LEFT + 1];    /* Last y position for
  87.                              * FFT bins */
  88. unsigned int  yscale[WINDOW_RIGHT - WINDOW_LEFT + 1];    /* Scaling factors */
  89. long         *ybase;        /* Scaling offset for log calculations */
  90.  
  91. long         *displayval;
  92. int          shift = 0;    /* Number of bits for gain shift */
  93. double          shiftscale = 1;    /* Multiplication factor that does this shift */
  94. float          log_scalefactor;    /* Scaling factor for log values */
  95. float          disp_scalefactor; /* Display scalefactor for log values */
  96.  
  97. void far     *buffer[BUFFERS];    /* Buffers for gathering data */
  98.  
  99. short         *p1, *p2;        /* Various indexing pointers */
  100. int         *bri;
  101. long         *pDisplayval;
  102. int         *pLasty;
  103. long         *pYbase;
  104. unsigned int *pYscale;
  105. int         *pX, *pX2;
  106.  
  107. unsigned char far *sample8;
  108. short far    *sample16;
  109.  
  110. long          a2, root, mask;    /* Variables for computing Sqrt/Log of
  111.                  * Amplitude^2 */
  112. long          peak_amp;        /* Peak amplitude found */
  113. int          peak_index;    /* Bin number of the peak amplitude */
  114. long          back1, back2;    /* Variables for differencing */
  115. char          ini_file[100];    /* Filename for the ini file */
  116. int          done = 0;        /* Flag indicating program should exit */
  117.  
  118. int
  119. main( int argc, char *argv[], char *environ[] )
  120. {
  121.   int        i, j, ind;
  122.   int        padX, padY;
  123.   long        y;
  124.   int        first;
  125.   int        key = 0;
  126.  
  127.   draw_init(  );
  128.  
  129.   DOUT( "Getting the command line arguments" );
  130.  
  131.   /*
  132.    * Check if the first parameter is an ini file name
  133.    */
  134.   if ( argc > 1 && argv[1][0] != '-' && argv[1][0] != '/' )
  135.   {
  136.     strncpy( ini_file, argv[1], sizeof( ini_file ) );
  137.     first = 2;
  138.   }
  139.   else
  140.   {
  141.     strncpy( ini_file, "dtmf_fft.ini", sizeof( ini_file ) );
  142.     first = 1;
  143.   }
  144.  
  145.   /*
  146.    * Parse the ini file and command line
  147.    */
  148.   DOUT( "Parsing the ini file" );
  149.   parse_ini_file(  );
  150.  
  151.   DOUT( "Parsing the command line" );
  152.   parse_command( ( argc - first ), &argv[first], environ );
  153.  
  154.   /*
  155.    * Initialize the buffer info for the maximum size we will encounter
  156.    */
  157.   DOUT( "Allocating the buffer space" );
  158.   setup_buffers( MAX_LEN );
  159.  
  160.   DOUT( "Computing the window functions" );
  161.   compute_window_function(  );
  162.  
  163.   /*
  164.    * Set up the required arrays in the FFT code.
  165.    */
  166.   DOUT( "Initializing the FFT code" );
  167.   InitializeFFT( fftlen );
  168.  
  169.   /*
  170.    * Initialize the graphics to 640x480 VGA mode
  171.    */
  172.   DOUT( "Setting the graphics mode" );
  173.   setup_graphics(  );
  174.  
  175.   setnormalpalette(  );
  176.   draw_fontcolor( TEXT_COLOR );
  177.   draw_rectangle( WINDOW_LEFT - 2, WINDOW_TOP - 2,
  178.           WINDOW_RIGHT + 2, WINDOW_BOTTOM + 2, BORDER_COLOR );
  179.  
  180.   DOUT( "Resetting the sound card" );
  181.   reset_soundcard(  );
  182.  
  183.   /*
  184.    * Initalize the graph scales
  185.    */
  186.   setup_xscale(     );
  187.   amplitude_scale(  );
  188.  
  189.   DOUT( "Drawing the header information" );
  190.   update_header(  );
  191.  
  192.   /*
  193.    * Keep getting data and plotting it. A space will pause, a second space
  194.    * will continue. Any other key will quit.
  195.    */
  196.   DOUT( "Entering data loop" );
  197.   while ( !done )
  198.   {
  199.     /* Wait for current buffer to fill up, and check for an input */
  200.     int          input;
  201.     key = draw_getkey(    );
  202. #ifdef DEBUG_MODE
  203.     i = 0;
  204.     while ( i++ < 500 && !key )
  205. #else
  206.     while ( ( !flag[process_buffer] || freeze ) && !key )
  207. #endif
  208.       key = draw_getkey(  );
  209.     input = key;
  210.     while ( input )
  211.     {
  212.       /* Grab and count repeated keystrokes */
  213.       int        repetitions = 1;
  214.  
  215.       key = input;
  216.       input = draw_getkey(  );
  217.       while ( input == key )
  218.       {
  219.     repetitions++;
  220.     input = draw_getkey(  );
  221.       }
  222.       done |= process_input( key, repetitions );
  223.     }
  224.  
  225.     if ( !key && !freeze )
  226.     {
  227.       int        clip = 0;
  228.       /*
  229.        * Perform windowing on the data
  230.        */
  231.       p1 = fftdata;
  232.       p2 = wind;
  233.  
  234.       if ( sample_size == 8 )
  235.       {
  236.     sample8 = ( unsigned char far * ) buffer[process_buffer];
  237.     for ( i = 0; i < fftlen; i++ )
  238.     {
  239. #ifdef DEBUG_MODE
  240.       *sample8 = ( char ) rand( );
  241. #endif
  242.       if ( ( *sample8 == 0 ) || ( *sample8 == 255 ) )
  243.         clip = 1;
  244.       *p1 = ( short ) ( ( ( ( long ) ( *sample8 ) - 128L ) * ( long ) ( *p2 ) ) >> 7 );
  245.       sample8++;
  246.       p1++;
  247.       p2++;
  248.     }
  249.       }
  250.       else
  251.       {
  252.     sample16 = ( short far * ) buffer[process_buffer];
  253.  
  254.     for ( i = 0; i < fftlen; i++ )
  255.     {
  256. #ifdef DEBUG_MODE
  257.       *sample16 = rand( );
  258. #endif
  259.       if ( ( *sample16 == 32767 ) || ( *sample16 == -32768L ) )
  260.         clip = 1;
  261.       *p1 = ( short ) ( ( ( long ) ( *sample16 ) * ( long ) ( *p2 ) ) >> 15 );
  262.       sample16++;
  263.       p1++;
  264.       p2++;
  265.     }
  266.       }
  267.  
  268.       if ( clip )
  269.     draw_setpalette( 0, warn.red, warn.green, warn.blue );
  270.       else
  271.     draw_setpalette( 0, background.red, background.green, background.blue );
  272.  
  273.       /* Free up the buffer we just processed. */
  274.       flag[process_buffer] = 0;
  275.       if ( ++process_buffer >= BUFFERS )
  276.     process_buffer = 0;
  277.  
  278.       /* Now that we have processed the buffer, queue it up again. */
  279.       recordblock( buffer[queue_buffer] );
  280.       if ( ++queue_buffer >= BUFFERS )
  281.     queue_buffer = 0;
  282.  
  283.       /* The real meat of the code lies elsewhere! */
  284.       RealFFT( fftdata );
  285.  
  286.       /* Use pointers for indexing to speed things up a bit. */
  287.       bri = BitReversed;
  288.       pDisplayval = displayval;
  289.  
  290.       for ( i = 0; i < fftlen / 2; i++ )
  291.       {
  292.     /* Compute the magnitude */
  293.     register long re = fftdata[*bri];
  294.     register long im = fftdata[( *bri ) + 1];
  295.     register long root;
  296.     if ( ( a2 = re * re + im * im ) < 0 )
  297.       a2 = 0;        /* Watch for possible overflow */
  298.     /* Use higher resolution only for small values */
  299.     if ( a2 > 4194304L )
  300.     {
  301.       root = 32;
  302.       do
  303.       {
  304.         mask = a2 / root;
  305.         root = ( root + mask ) >> 1;
  306.       } while ( labs( root - mask ) > 1 );
  307.       root *= 16;
  308.     }
  309.     else
  310.     {
  311.       root = 512;
  312.       a2 *= 256;
  313.       do
  314.       {
  315.         mask = a2 / root;
  316.         root = ( root + mask ) >> 1;
  317.       } while ( labs( root - mask ) > 1 );
  318.     }
  319.     *pDisplayval = root;
  320.     bri++;
  321.     pDisplayval++;
  322.       }
  323.     }
  324.  
  325.     if ( dtmf_mode )
  326.     {
  327.       active_freq = 0;
  328.       for ( i = 0; i < 8; i++ )
  329.       {
  330.     /* ind = (int)( (double)(f_dtmf[i])*fftlen/SampleRate +.5 ); */
  331.     if ( displayval[o_dtmf[i]] > 524288.0 * threshold_level )
  332.     {
  333.       on_dtmf[i] = 1;
  334.       active_freq += ( i < 4 ) ? 1 : 10;
  335.     }
  336.     else
  337.       on_dtmf[i] = 0;
  338.       }
  339.       if ( active_freq == 11 )    /* DTMF means 2 freq's active */
  340.       {
  341.     padX = 1 * on_dtmf[4] + 2 * on_dtmf[5] + 3 * on_dtmf[6] + 4 * on_dtmf[7] - 1;
  342.     padY = 1 * on_dtmf[0] + 2 * on_dtmf[1] + 3 * on_dtmf[2] + 4 * on_dtmf[3] - 1;
  343.     i = padX + 4 * padY;
  344.     if( log_mode && !active_dtmf )
  345.       fprintf( log_file, "%s - '", time_stamp() );
  346.     if ( i != last_i || !active_dtmf )
  347.     {
  348.       dtmf_nr[p_dtmf] = matrix_dtmf[padY][padX];
  349.       dtmf_nr[p_dtmf + 1] = 0;
  350.       last_i = i;
  351.       draw_fontcolor( LABEL_COLOR );
  352.       draw_text_left( 156 + 8 * ( p_dtmf % 40 ), 60 + 10 * ( p_dtmf / 40 ),
  353.               &dtmf_nr[p_dtmf] );
  354.       if( log_mode )
  355.         fprintf( log_file, "%c", dtmf_nr[p_dtmf] );
  356.       p_dtmf++;
  357.       if ( p_dtmf == 120 )
  358.       {
  359.         p_dtmf = 0;
  360.         draw_bar( 156, 60, 476, 90, 0 );
  361.       }
  362.     }
  363.     active_dtmf = 1;
  364.       }
  365.       else
  366.       {
  367.     if( log_mode && active_dtmf )
  368.       fprintf( log_file, "'\n" );
  369.     active_dtmf = 0;
  370.       }
  371.     }
  372.  
  373.     if ( ctcss_mode )
  374.     {
  375.       draw_bar( 200, 60, 240, 70, 0 );
  376.       draw_fontcolor( LABEL_COLOR );
  377.       draw_text_left( 200, 60, "off" );
  378.       m_ctcss = 0L;
  379.       for ( i = 0; i < CTCSS_MAX; i++ )
  380.       {
  381.     if( ( i < 32 ? ctcss_act1 >> i : ctcss_act2 >> (i-32) ) & 1 )
  382.     {
  383.       ind = ( int ) ( f_ctcss[i] * fftlen / SampleRate + .5 );
  384.       if ( displayval[ind] > m_ctcss )
  385.       {
  386.         m_ctcss = displayval[ind];
  387.         ctcss_nr = i;
  388.       }
  389.     }
  390.       }
  391.       if ( m_ctcss > 524288.0 * threshold_level )
  392.       {
  393.     draw_bar( 124, 60, 240, 70, 0 );
  394.     draw_fontcolor( GRAPH_COLOR );
  395.     sprintf( sline, "%5.1lf Hz", f_ctcss[ctcss_nr] );
  396.     draw_text_left( 124, 60, sline );
  397.     draw_fontcolor( LABEL_COLOR );
  398.     draw_text_left( 200, 60, "on" );
  399.     if( log_mode && (last_i != ctcss_nr || !active_ctcss ) )
  400.     {
  401.       fprintf( log_file, "%s - %5.1lf Hz\n", time_stamp(),
  402.             f_ctcss[ctcss_nr] );
  403.       last_i = ctcss_nr;
  404.     }
  405.     active_ctcss = 1;
  406.       }
  407.       else
  408.     active_ctcss = 0;
  409.     }
  410.  
  411.     {
  412.       /*
  413.        * Next, put this data up on the display
  414.        */
  415.       setup_vga(  );        /* Prepare VGA for video update */
  416.  
  417.       pLasty = lasty;
  418.       pX = x;
  419.       pX2 = x2;
  420.       pYscale = yscale;
  421.       peak_amp = 0;
  422.       peak_index = 0;
  423.       y = WINDOW_BOTTOM;
  424.       /* For linear amplitude mode */
  425.       {
  426.     int          index, xval;
  427.     for ( i = WINDOW_LEFT; i < WINDOW_RIGHT + 1; i++ )
  428.     {
  429.       /*
  430.        * If this line is the same as the previous one, just use the
  431.        * previous y value. Else go ahead and compute the value.
  432.        */
  433.       index = *pX;
  434.       if ( index != -1 )
  435.       {
  436.         register long dv = displayval[index];
  437.         if ( *pX2 )        /* Take the maximum of a set of bins */
  438.         {
  439.           for ( xval = index; xval < *pX2; xval++ )
  440.           {
  441.         if ( displayval[xval] > dv )
  442.         {
  443.           dv = displayval[xval];
  444.           index = xval;
  445.         }
  446.           }
  447.         }
  448.         y = ( WINDOW_BOTTOM ) - ( ( dv * *pYscale ) >> shift );
  449.         if ( y < WINDOW_TOP )
  450.           y = WINDOW_TOP;
  451.         if ( dv > peak_amp )
  452.         {
  453.           peak_amp = dv;
  454.           peak_index = *pX;
  455.         }
  456.       }
  457.       if ( y > *pLasty )
  458.       {
  459.         /* Draw a black line */
  460.         unsigned char bit = ~( 0x80 >> ( i & 0x07 ) );
  461.         unsigned int  endbase = ( unsigned int ) ( y * 80 );
  462.         unsigned int  base = ( unsigned int ) ( *pLasty * 80 + ( i >> 3 ) );
  463.         while ( base < endbase )
  464.         {
  465.           screen( base ) &= bit;
  466.           base += 80;
  467.         }
  468.       }
  469.       else
  470.       {
  471.         /* Draw a blue line. */
  472.         unsigned char bit = 0x80 >> ( i & 0x07 );
  473.         unsigned int  endbase = ( unsigned int ) ( ( *pLasty + 1 ) * 80 );
  474.         unsigned int  base = ( unsigned int ) ( y * 80 + ( i >> 3 ) );
  475.  
  476.         while ( base < endbase )
  477.         {
  478.           screen( base ) |= bit;
  479.           base += 80;
  480.         }
  481.       }
  482.  
  483.       *pLasty = ( unsigned int ) y;
  484.       pDisplayval++;
  485.       pX++;
  486.       pX2++;
  487.       pLasty++;
  488.       pYscale++;
  489.     }
  490.       }
  491.       cleanup_vga(  );        /* Reset VGA for normal functions */
  492.     }
  493.  
  494.     if ( display_peak )
  495.     {
  496.       char        ach[20];
  497.       sprintf( ach, "%7.1f", ( double ) SampleRate * peak_index / fftlen );
  498.       draw_bar( PKX, PKY - 1, PKX + 63, PKY + _font_height, 0 );
  499.       draw_text_left( PKX, PKY, ach );
  500.     }
  501.   }
  502.   /*
  503.    * Shut down the DMA system.
  504.    */
  505.   cleanup_soundcard(  );
  506.  
  507.   cleanup_graphics(  );
  508.  
  509.   if( log_mode )
  510.     fclose( log_file );
  511.  
  512.   printf( "You have been using DTMF_FFT v 1.10 (dtmf & ctcss) " );
  513. #ifdef SC_SB8
  514.   if ( Soundcard == SC_SB8 )
  515.     printf( " in Soundblaster 8-bit mode." );
  516. #endif
  517. #ifdef SC_SB16
  518.   if ( Soundcard == SC_SB16 )
  519.     printf( " in Soundblaster 16-bit mode." );
  520. #endif
  521.   printf( "\nCopyright (C) 1996 Philip VanBaren & "
  522.       "(C) 1997 Emil Laurentiu (YO3GGH)" );
  523.   return ( 0 );
  524. }
  525.